home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 2.toast / pc / sample code / quicktime / goodies / aiffwriter / aiffhardware.c next >
Encoding:
C/C++ Source or Header  |  2000-09-28  |  18.2 KB  |  545 lines

  1. /*
  2. **    Apple Macintosh Developer Technical Support
  3. **
  4. **    Routines for dealing with virtual sound hardware from a 'sdev' component.
  5. **
  6. **    by Mark Cookson, Apple Developer Technical Support
  7. **    --based on code by Kip Olson
  8. **
  9. **    File:    Hardware.c
  10. **
  11. **    Copyright ©1993-1996 Apple Computer, Inc.
  12. **    All rights reserved.
  13. **
  14. **    You may incorporate this sample code into your applications without
  15. **    restriction, though the sample code has been provided "AS IS" and the
  16. **    responsibility for its operation is 100% yours.  However, what you are
  17. **    not permitted to do is to redistribute the source as "Apple Sample
  18. **    Code" after having made changes. If you're going to re-distribute the
  19. **    source, we require that you make it clear in the source that the code
  20. **    was descended from Apple Sample Code, but that you've made changes.
  21. */
  22.  
  23. #include <AIFF.h>
  24. #include <Files.h>
  25. #include <Devices.h>
  26. #include <Timer.h>
  27. #include <Sound.h>
  28. #include <FixMath.h>
  29. #include <Script.h>
  30.  
  31. #include "AIFFWriter.h"
  32.  
  33.  
  34. #ifdef FakeInterrupts
  35. extern SoundOutputGlobalsPtr                    gGlobals;
  36. #endif
  37.  
  38.  
  39. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  40. // Initialize the hardware.
  41.  
  42. OSErr SetupHardware (SoundOutputGlobalsPtr globals)
  43. {
  44.     IOBufferPtr            ioBuffer;
  45.     ParmBlkPtr            iopb;
  46.     unsigned long        samples;
  47.     short                i;
  48.     OSErr                result;
  49.  
  50.     // setup our output data which we'll request from the mixer and
  51.     // setup the output file as well
  52.     
  53.     globals->outputData.flags = 0;
  54.     if ((**globals->prefs).sampleSize == 8)
  55.         globals->outputData.format = k8BitOffsetBinaryFormat;
  56.     else
  57.         globals->outputData.format = k16BitBigEndianFormat;
  58.     globals->outputData.numChannels = (**globals->prefs).numChannels;
  59.     globals->outputData.sampleSize = (**globals->prefs).sampleSize;
  60.     globals->outputData.sampleRate = (**globals->prefs).sampleRate;
  61.     globals->outputData.sampleCount = (**globals->prefs).sampleCount;
  62.     globals->outputData.buffer = nil;
  63.     globals->outputData.reserved = 0;
  64.  
  65.     // Setup hardware here. This example just calculates the interrupt interval
  66.     // for the time manager task interrupt based on the current sample rate.
  67.  
  68.     globals->interruptInterval = UnsignedFixedMulDiv(((long)kHardwareSampleCount) << 16,
  69.                                                     1000000, globals->outputData.sampleRate);
  70.  
  71.     samples = UnsignedFixedMulDiv(globals->outputData.sampleRate, kSecondsInIOBuffer, fixed1);
  72.     samples *= (globals->outputData.sampleSize >> 3) * globals->outputData.numChannels;
  73.     globals->ioBufferSize = (samples + kHardwareSampleCount - 1) & (~(kHardwareSampleCount - 1));
  74.  
  75.     result = SetupOutputFile(globals);                            // initialize output file
  76.     FailIf(result != noErr, Exit);
  77.  
  78.     for (i = 0; i < 2; ++i)                                        // loop over both parameter blocks
  79.     {
  80.         ioBuffer = &globals->ioBuffers[i];
  81.  
  82.         // get some memory for i/o buffers
  83.         ioBuffer->buffer = NewPtrClear(globals->ioBufferSize);
  84.         FailIf(ioBuffer->buffer == nil, NewPtrFailed);
  85.         iopb = &ioBuffer->iopb;                                    // get pointer to param block
  86.  
  87.         /* fill up the param block with the fields that never change after this. Note that
  88.            fields that are set to zero are commented out since we just zeroed things above. */
  89.  
  90.         iopb->ioParam.ioRefNum        = globals->fRefNum;            // refnum of file to write to
  91. //        iopb->ioParam.ioCompletion    = nil;                        // no completion routine
  92. //        iopb->ioParam.ioReqCount    = 0;                        // no request yet
  93.         iopb->ioParam.ioBuffer        = ioBuffer->buffer;            // buffer to write to
  94.         iopb->ioParam.ioPosMode        = (1 << noCacheBit);        // write w/o cache
  95. //        iopb->ioParam.ioPosOffset    = 0;                        // no offset yet
  96.     }
  97.     return (noErr);
  98.  
  99.  
  100. NewPtrFailed:
  101.     if (globals->ioBuffers[0].buffer != nil)
  102.         DisposePtr(globals->ioBuffers[0].buffer);            // dispose of buffers
  103.     if (globals->ioBuffers[1].buffer != nil)
  104.         DisposePtr(globals->ioBuffers[1].buffer);            // dispose of buffers
  105. Exit:
  106.     return (result);
  107. }
  108.  
  109. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  110. // Release the hardware.
  111. // BUG ALERT!  If we sample MicroSeconds at just the wrong time, we could be here for a long time!
  112.  
  113. void ReleaseHardware (SoundOutputGlobalsPtr globals)
  114. {
  115.     unsigned long        timeLimit;
  116.     unsigned long        startTime;
  117.  
  118.     startTime = MicroSeconds();
  119.     timeLimit = globals->interruptInterval << 1;                // wait for 2 interrupt periods
  120.     while ((globals->hardwareOn) && ((MicroSeconds() - startTime) < timeLimit))
  121.     {
  122.         ;    // wait until interrupts turn off, or we time out
  123.     }
  124.  
  125.     StopHardware(globals);                                        // make sure hardware is off
  126.  
  127.     if (globals->fRefNum != 0)
  128.     {
  129.         CloseOutputFile(globals);                                // close output file
  130.         globals->fRefNum = 0;
  131.     }
  132. }
  133.  
  134. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  135. // Turn on hardware interrupts.
  136.  
  137. void StartHardware(SoundOutputGlobalsPtr globals)
  138. {
  139.     if (!globals->hardwareOn)
  140.     {
  141.         globals->hardwareOn = true;                            // the hardware will soon be on
  142.  
  143.         // Turn hardware on here. The example uses the Time Manager for interrupts
  144.  
  145. #ifndef FakeInterrupts
  146.         // start the time manager task going
  147.  
  148.         globals->tmTask.task.tmAddr = NewTimerProc(TMInterrupt);
  149.         globals->tmTask.task.tmCount = 0;
  150.         globals->tmTask.task.tmWakeUp = 0;
  151.         globals->tmTask.task.tmReserved = 0;
  152.         globals->tmTask.globals = globals;
  153.  
  154.         InsXTime((QElemPtr) &globals->tmTask);
  155.         PrimeTime((QElemPtr) &globals->tmTask, -globals->interruptInterval);
  156. #endif
  157.     }
  158. }
  159.  
  160. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  161. // Turn off hardware interrupts.
  162.  
  163. void StopHardware(SoundOutputGlobalsPtr globals)
  164. {
  165.     if (globals->hardwareOn)
  166.     {
  167.         // Turn hardware off here. The example removes the Time Manager task
  168.  
  169. #ifndef FakeInterrupts
  170.         RmvTime((QElemPtr) &globals->tmTask);
  171. #endif
  172.         globals->hardwareOn = false;                        // the hardware is now off
  173.     }
  174. }
  175.  
  176. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  177. // Resume hardware interrupts after they were suspended.
  178.  
  179. void ResumeHardware(SoundOutputGlobalsPtr globals)
  180. {
  181.     if (globals->hardwareOn)
  182.     {
  183.         // Resume hardware interrupts here. The example queues another Time Manager interrupt
  184.  
  185. #ifndef FakeInterrupts
  186.         PrimeTime((QElemPtr) &globals->tmTask, -globals->interruptInterval);
  187. #endif
  188.     }
  189. }
  190.  
  191. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  192. // Copy samples to the hardware buffer. In our case, write to a file.
  193.  
  194. #define SamplesToBytes(samples, shift)    (samples << shift)
  195. #define BytesToSamples(bytes, shift)    (bytes >> shift)
  196.  
  197. void CopySamplesToHardware(SoundOutputGlobalsPtr globals, SoundComponentDataPtr siftPtr)
  198. {
  199.     long            samplesToCopy;
  200.     long            bytesToCopy;
  201.     long            bytesLeft;
  202.     IOBufferPtr        currentBuffer;
  203.     short            sourceShift;
  204.     short            destShift;
  205.  
  206.     sourceShift = siftPtr->sampleSize / 16;
  207.     if (siftPtr->numChannels == 2)
  208.         sourceShift++;
  209.  
  210.     destShift = globals->outputData.sampleSize / 16;                // add 1 more for 16 bit, 2 for 32
  211.     if (globals->outputData.numChannels == 2)
  212.         destShift++;
  213.  
  214.     samplesToCopy = siftPtr->sampleCount;                            // don't copy more than hardware buffer has
  215.     if (samplesToCopy > kHardwareSampleCount)
  216.         samplesToCopy = kHardwareSampleCount;
  217.  
  218.     currentBuffer = &globals->ioBuffers[globals->currentIndex];        // get current i/o buffer
  219.     if (currentBuffer->iopb.ioParam.ioResult != 0)
  220.     {                                                                // oh, oh, write is still in progress
  221.         FailMessage(currentBuffer->iopb.ioParam.ioResult != 0);
  222.         siftPtr->buffer += SamplesToBytes(samplesToCopy, sourceShift);    // update source pointer
  223.         siftPtr->sampleCount -= samplesToCopy;                        // subtract amount copied from source and throw these bytes away!
  224.         return;                                                        // throw these bytes away!
  225.     }
  226.  
  227.     bytesToCopy = SamplesToBytes(samplesToCopy, destShift);        // turn samples into bytes
  228.     bytesLeft = globals->ioBufferSize - currentBuffer->byteCount;        // calc how much is left in i/o buffer
  229.  
  230.     if (bytesToCopy > bytesLeft)                                // limit to amount we have left in buffer
  231.         bytesToCopy = bytesLeft;
  232.     samplesToCopy = BytesToSamples(bytesToCopy, destShift);        // turn back into samples
  233.  
  234.     if (globals->fRefNum)                                         // only write out if file is open
  235.         OutputToFile(siftPtr, currentBuffer->buffer + currentBuffer->byteCount, bytesToCopy);
  236.  
  237.     currentBuffer->byteCount += bytesToCopy;                    // update dest pointer
  238.     siftPtr->buffer += SamplesToBytes(samplesToCopy, sourceShift);    // update source pointer
  239.     siftPtr->sampleCount -= samplesToCopy;                        // subtract amount copied from source
  240.  
  241.     if (currentBuffer->byteCount == (long)globals->ioBufferSize)// current buffer is full - write it out
  242.     {
  243.         if (globals->fRefNum)                                    // only write out if file is open
  244.         {
  245.             currentBuffer->iopb.ioParam.ioReqCount = currentBuffer->byteCount;    // write this many bytes out
  246.             currentBuffer->iopb.ioParam.ioPosOffset = 0;        // offset from current position
  247.  
  248. #ifdef FakeInterrupts
  249.             PBWriteSync(¤tBuffer->iopb);                    // write out the data synchronously
  250. #else
  251.             PBWriteAsync(¤tBuffer->iopb);                    // start the asynch write
  252. #endif
  253.         }
  254.  
  255.         currentBuffer->byteCount = 0;                            // buffer will be empty next time we use it
  256.         globals->currentIndex ^= 1;                                // switch to other buffer
  257.     }
  258. }
  259.  
  260. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  261. // Slam the file.
  262.  
  263. void OutputToFile(SoundComponentDataPtr siftPtr, void *dest, long bytesToCopy)
  264. {
  265.     long    i;
  266.     Byte    *sp;
  267.     Byte    *dp;
  268.  
  269.     if (siftPtr->format == k8BitOffsetBinaryFormat)                // 8 bit offset source
  270.     {
  271.         // convert the data from offset binary to two's complement
  272.         
  273.         sp = (Byte *)siftPtr->buffer;
  274.         dp = (Byte *)dest;
  275.         for (i = bytesToCopy - 1; i >= 0; --i)
  276.             *dp++ = *sp++ ^ 0x80;
  277.     }
  278.     else                                                        // copy other formats
  279.         BlockMove(siftPtr->buffer, dest, bytesToCopy);
  280. }
  281.  
  282. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  283. // Fake interrupt routine used when running the debugging application.
  284.  
  285. #ifdef FakeInterrupts
  286. Boolean FakeInterrupt(void)
  287. {
  288.     SoundOutputGlobalsPtr    globals = gGlobals;                    // get our globals
  289.  
  290.     if (globals->hardwareOn)
  291.     {
  292.         if (MicroSeconds() >= globals->nextTime)
  293.         {
  294.             globals->nextTime = MicroSeconds();
  295.             InterruptRoutine(globals);
  296.             globals->nextTime += globals->interruptInterval;
  297.         }
  298.     }
  299.     return (globals->hardwareOn);
  300. }
  301. #endif
  302.  
  303. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  304. // Entry point for the Time Manager task interrupt routine
  305.  
  306. //#if __MWERKS__ && TARGET_CPU_68K
  307.  
  308. //void TMInterrupt (myTMTaskPtr taskPtr:__a1)
  309. //{
  310. //    InterruptRoutine(taskPtr->globals);                        // call interrupt routine with globals
  311. //}
  312.  
  313. //#else
  314.  
  315. void TMInterrupt (myTMTaskPtr taskPtr)
  316. {
  317. #if TARGET_CPU_68K
  318.     taskPtr = (myTMTaskPtr)GetRegisterA1();
  319. #endif
  320.     InterruptRoutine(taskPtr->globals);                        // call interrupt routine with globals
  321. }
  322. //#endif
  323.  
  324. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  325. // This routine is called every hardware interrupt to fill the hardware
  326. // with audio data. First it should suspend interrupts so it will not
  327. // be interrupted again (this example just queues up the next interrupt).
  328. // Then it should get more data from the source mixer, and copy the data
  329. // to the hardware. In the way out, if all data was copied, try to get
  330. // some more so it will be available next time.
  331.  
  332. void InterruptRoutine(SoundOutputGlobalsPtr globals)
  333. {
  334.     SoundComponentDataPtr        siftPtr;
  335.  
  336.     siftPtr = GetMoreSource(globals);                        // get source from mixer
  337.     if (siftPtr == nil)                                        // no more source
  338.         StopHardware(globals);                                // turn hardware off
  339.     else
  340.     {
  341.         CopySamplesToHardware(globals, siftPtr);            // fullfill hardware request
  342.  
  343.         if (siftPtr->sampleCount == 0)                        // exhausted the source
  344.             siftPtr = GetMoreSource(globals);                // get more for next time
  345.     }
  346.     ResumeHardware(globals);                                // queue next interrupt while we are processing
  347. }
  348.  
  349. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  350. // This routine returns the component data pointer to your mixer source. If there
  351. // is no source or it is empty, it will call down the chain to fill it up.
  352.  
  353. SoundComponentDataPtr GetMoreSource(SoundOutputGlobalsPtr globals)
  354. {
  355.     ComponentResult            result;
  356.     SoundComponentDataPtr    siftPtr;
  357.  
  358.     siftPtr = globals->sourceDataPtr;
  359.     if ((siftPtr == nil) || (siftPtr->sampleCount == 0))    // no data - better get some
  360.     {
  361.         result = SoundComponentGetSourceData(globals->sourceComponent, &globals->sourceDataPtr);
  362.         FailIf(result != noErr, Failure);
  363.  
  364.         siftPtr = globals->sourceDataPtr;
  365.         if ((siftPtr == nil) || (siftPtr->sampleCount == 0)) // source has no more data
  366.             return (nil);                                    // stop the presses
  367.     }
  368.     return (siftPtr);                                        // return pointer to source
  369.  
  370. Failure:
  371.     return (nil);
  372. }
  373.  
  374. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  375. // This routine initializes the output file and sets up the i/o param block
  376. // used to write the data to disk.
  377.  
  378. OSErr SetupOutputFile (SoundOutputGlobalsPtr globals)
  379. {
  380.     FSSpec            fspec;
  381.     OSErr            err;
  382.  
  383.     err = FSMakeFSSpec(0, 0, kSndFileName, &fspec);            // make the FS spec
  384.     if (err == fnfErr)                                        // file not found is OK
  385.         err = noErr;
  386.     else
  387.         FailIf(FSpDelete(&fspec) != noErr, Failure);
  388.     FailIf(err != noErr, Failure);
  389.  
  390.     err = FSpCreate(&fspec, kAIFFCreator, AIFFID, smSystemScript);    // create file
  391.     FailIf(err != noErr, FSpCreateFailed);
  392.  
  393.     err = FSpOpenDF(&fspec, fsRdWrPerm, &globals->fRefNum);        // open file
  394.     FailIf(err != noErr, FSpOpenDFFailed);
  395.  
  396.     err = SetupAIFFHeader(globals->fRefNum, globals->outputData.numChannels, globals->outputData.sampleRate,
  397.                           globals->outputData.sampleSize, globals->outputData.format, 0, 0);
  398.     FailIf(err != noErr, SetupAIFFFailed);
  399.  
  400.     err = GetFPos(globals->fRefNum, &globals->headerLen);        // get length of AIFF header
  401.     FailIf(err != noErr, SetupAIFFFailed);
  402.  
  403.     return (noErr);
  404.  
  405. SetupAIFFFailed:
  406.     if (globals->fRefNum != 0)
  407.         FSClose(globals->fRefNum);
  408.  
  409. FSpOpenDFFailed:
  410. FSpCreateFailed:
  411. Failure:
  412.     return (err);
  413. }
  414.  
  415. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  416. // This routine makes sure all asynchronous i/o is complete, writes out any
  417. // remaining data, updates the AIFF header and closes the file.
  418.  
  419. OSErr CloseOutputFile(SoundOutputGlobalsPtr globals)
  420. {
  421.     long            filePos, bytesWritten;
  422.     IOBufferPtr        currentBuffer;
  423.     OSErr            err;
  424.  
  425.     while  ((globals->ioBuffers[globals->currentIndex].iopb.ioParam.ioResult > 0) ||    // wait for PBWrite to complete
  426.             (globals->ioBuffers[globals->currentIndex^1].iopb.ioParam.ioResult > 0))    // for both buffers
  427.         ;
  428.  
  429.     currentBuffer = &globals->ioBuffers[globals->currentIndex];    // get current i/o buffer
  430.  
  431.     if (currentBuffer->byteCount != 0)                    // still some data to write out
  432.     {
  433.         currentBuffer->iopb.ioParam.ioReqCount = currentBuffer->byteCount;    // write this many bytes out
  434.         currentBuffer->iopb.ioParam.ioPosOffset = 0;    // offset from current position
  435.  
  436.         err = PBWriteSync(¤tBuffer->iopb);        // write this buffer synchronously
  437.         FailIf(err != noErr, PBWriteSyncFailed);
  438.     }
  439.  
  440.     err = GetFPos(globals->fRefNum, &filePos);            // get current file position
  441.     FailIf(err != noErr, GetFPosFailed);
  442.  
  443.     bytesWritten = filePos - globals->headerLen;        // calc no. bytes written to file
  444.     filePos = ++filePos & ~1;                            // make sure file length is a word-aligned
  445.  
  446.     err = SetEOF(globals->fRefNum, filePos);            // set current file position to EOF
  447.     FailIf(err != noErr, SetEOFFailed);
  448.  
  449.     err = SetFPos(globals->fRefNum, fsFromStart, 0);    // rewind file to beginning
  450.     FailIf(err != noErr, SetFPosFailed);
  451.  
  452.     err = SetupAIFFHeader(globals->fRefNum, globals->outputData.numChannels, globals->outputData.sampleRate,
  453.                           globals->outputData.sampleSize, globals->outputData.format, bytesWritten, 0);
  454.     FailIf(err != noErr, SetupAIFFHeaderFailed);
  455.     // all non-error conditions can fall through to the clean up code
  456.  
  457. SetupAIFFHeaderFailed:
  458. SetFPosFailed:
  459. SetEOFFailed:
  460. GetFPosFailed:
  461. PBWriteSyncFailed:
  462.     FSClose(globals->fRefNum);                            // close output file
  463.     globals->fRefNum = 0;
  464.  
  465.     return (err);
  466. }
  467.  
  468. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  469. OSErr GetPreferences (SoundOutputGlobalsPtr globals)
  470. {
  471.     HardwarePrefsHandle        prefs;
  472.     HardwarePrefsHandle        oldPrefs;
  473.     OSErr                    result;
  474.  
  475.     prefs = (HardwarePrefsHandle)GetComponentRefcon((Component)globals->self);
  476.     if (prefs == nil)
  477.     {    
  478.         // no in memory preferences found, then create them
  479.         prefs = (HardwarePrefsHandle)NewHandleSysClear(sizeof(HardwarePreferences));
  480.         FailWithAction(prefs == nil, result = MemError(), NewPrefsFailed);
  481.  
  482.         // set our preferences to default values and put them in our refCon
  483.         (**prefs).size = sizeof(HardwarePreferences);
  484.         (**prefs).version = kSoundComponentVersion;
  485.         (**prefs).volume = 0x00100100;
  486.         (**prefs).sampleRate = rate22050hz;                // default sample rate
  487.         (**prefs).sampleSize = 16;                        // default sample size
  488.         (**prefs).numChannels = 2;                        // default num channels
  489.         (**prefs).sampleCount = kHardwareSampleCount;    // default buffer count
  490.         SetComponentRefcon((Component)globals->self, (unsigned long)prefs);
  491.         globals->prefs = prefs;                                            // set global
  492.  
  493.         oldPrefs = (HardwarePrefsHandle)NewHandleSysClear(sizeof(HardwarePreferences));
  494.         FailWithAction(oldPrefs == nil, result = MemError(), NewOldPrefsFailed);
  495.  
  496.         result = GetSoundPreference(kAIFFWriterSubType, kAIFFWriterName, (Handle)oldPrefs);
  497.         if (result == noErr)
  498.         {
  499.             // we found our old preferences, check version and size of old preferences
  500.             (**prefs).volume = (**oldPrefs).volume;
  501.             (**prefs).sampleRate = (**oldPrefs).sampleRate;
  502.             (**prefs).sampleSize = (**oldPrefs).sampleSize;
  503.             (**prefs).numChannels = (**oldPrefs).numChannels;
  504.             (**prefs).sampleCount = (**oldPrefs).sampleCount;
  505.         }
  506.         DisposeHandle((Handle)oldPrefs);
  507.         
  508.         result = InitFromPreferences(globals);
  509.         FailIf(result != noErr, InitHWFailed);
  510.     }
  511.     return (noErr);
  512.     
  513. InitHWFailed:
  514. NewOldPrefsFailed:
  515. NewPrefsFailed:
  516.     return (result);
  517. }
  518.  
  519. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  520. // Setup the hardware according to the user's preferences.
  521. // Configure as much of the hardware as possible at this time, and use the
  522. // InitOutputDevice() method call for the configuration that needs to be done
  523. // everytime the component is being opened to play audio. If altering a setting
  524. // would cause a click, then it would be better to do that here instead of everytime
  525. // the user started a new sound.
  526.  
  527. OSErr InitFromPreferences(SoundOutputGlobalsPtr globals)
  528. {
  529.     globals; // suppress "unused variable" warning for all compilers
  530.     return (noErr);
  531. }
  532.  
  533. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  534. #if !TARGET_CPU_68K
  535. // for 68K, the macro we are using only returns a long, so the PPC version
  536. // will just return the lo part of the number to be consistent
  537. unsigned long MicroSeconds(void)
  538. {
  539.     UnsignedWide            microTickCount;
  540.  
  541.     Microseconds(µTickCount);
  542.     return (microTickCount.lo);
  543. }
  544. #endif
  545.